package net.freebytes.ssmp.shiro;


import net.freebytes.ssmp.entity.Permission;
import net.freebytes.ssmp.entity.Role;
import net.freebytes.ssmp.entity.User;
import net.freebytes.ssmp.service.PermService;
import net.freebytes.ssmp.service.RoleService;
import net.freebytes.ssmp.service.UserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

/**
 * 自定义realm
 *
 * @author 千里明月
 * @date 2021/7/20
 **/
public class CustomRealm extends AuthorizingRealm {

    private static Logger log = LoggerFactory.getLogger(CustomRealm.class);

    @Autowired
    private UserService userService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private PermService permService;

    /**
     * @MethodName doGetAuthorizationInfo
     * @Description 权限配置类
     * @Param [principalCollection]
     * @Return AuthorizationInfo
     * <p>
     * 每次url访问的时候，都会经过这个方法，进行权限校验
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //获取登录用户
        User user = (User) principalCollection.getPrimaryPrincipal();

        List<Role> roles = roleService.cacheRoleByUserId(user.getId());
        List<Permission> permissions = permService.cacheAllPermissionByUid(user.getId());

        //添加角色和权限
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        for (Role r : roles) {
            //添加角色
            simpleAuthorizationInfo.addRole(r.getName());
            //添加权限
            for (Permission p : permissions) {
                simpleAuthorizationInfo.addStringPermission(p.getName());
            }
        }
        return simpleAuthorizationInfo;
    }

    /**
     * 认证
     * <p>
     * 这个方法是在调用subject.login(usernamePasswordToken);方法后生效的
     * 作用是根据客户端输入的用户名查找到数据库中的用户记录，拿到该记录的密文密码和盐，
     * 然后利用配置好的credentialsMatcher和盐，对输入的明文密码进行加密加盐计算，
     * 再和数据库中的密文密码进行匹配。
     *
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // 获取用户名
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String username = token.getUsername();
        User user = null;
        try {
            user = userService.getUserByName(username);
        } catch (IncorrectCredentialsException e) {
            throw new IncorrectCredentialsException(e.getMessage(), e);
        } catch (LockedAccountException e) {
            throw new LockedAccountException(e.getMessage(), e);
        } catch (ExcessiveAttemptsException e) {
            throw new ExcessiveAttemptsException(e.getMessage(), e);
        } catch (Exception e) {
            log.info("对用户[" + username + "]进行登录验证..验证未通过{}", e.getMessage());
            throw new AuthenticationException(e.getMessage(), e);
        }
        //进行验证
        SaltedAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                //用户
                user,
                //密码
                user.getPassword(),
                //设置盐值
                ByteSource.Util.bytes(user.getSalt()),
                getName()
        );
        return authenticationInfo;
    }
}
